package jblip.resources;

import java.io.Serializable;
import java.util.Vector;

import jblip.resources.Media.MediaType;
import jblip.util.json.JSONArray;
import jblip.util.json.JSONObject;
import jblip.util.json.JSONValue;

/**
 * Class providing wrapper for optionally included resources.
 * 
 * <p>
 * Since API 0.02 some of the blip resources can appear in two forms:
 * </p>
 * <ul>
 * <li>as an object including the resource, or</li>
 * <li>as a path to the resource in the API</li>
 * </ul>
 * <p>
 * As the existance of one or the other form in blip server responses is
 * mutually exlusive, this class provides convenience functions for obtaining
 * the resource in one of them from the JSON objects, using provided
 * {@link ResourcesFactory}
 * </p>
 * 
 * @author Krzysztof Sroka
 * @param <T>
 *          Class of the resource returned
 * @since 0.2
 */
public abstract class PathResource<T> implements Serializable {
  private static final long serialVersionUID = 1L;

  public static PathResource<User> getUser(final JSONObject obj,
          final String key) {
    return new PathResource<User>(obj, key) {
      private static final long serialVersionUID = 1L;

      @Override
      public User getResource(ResourcesFactory factory) {
        if (hasResource()) {
          return factory.getUser(resource);
        } else {
          return null;
        }
      }
    };
  }

  public static PathResource<UserPicture> getUserPicture(final JSONObject obj,
          final String key) {
    return new PathResource<UserPicture>(obj, key) {
      private static final long serialVersionUID = 1L;

      @Override
      public UserPicture getResource(ResourcesFactory factory) {
        if (hasResource()) {
          return factory.getUserPicture(obj);
        } else {
          return null;
        }
      }
    };
  }

  public static PathResource<Media> getMedia(final JSONObject obj,
          final String key, final MediaType type) {
    return new PathResource<Media>(obj, key) {
      private static final long serialVersionUID = 1L;

      @Override
      public Media getResource(ResourcesFactory factory) {
        if (hasResource()) {
          return factory.getMedia(resource, type);
        } else {
          return null;
        }
      }
    };
  }

  public static PathResource<Vector<Media>> getMediaVector(final JSONObject json,
          final String key, final MediaType type) {
    return new PathResource<Vector<Media>>(json, key) {
      private static final long serialVersionUID = 1L;

      @Override
      public Vector<Media> getResource(ResourcesFactory factory) {
        if (hasResource()) {
          Vector<Media> mlist = new Vector<Media>();
          JSONArray jsonlist = (JSONArray) this.resource;
          for (int i = 0; i < jsonlist.length(); i++) {
            mlist.add(factory.getMedia(jsonlist.get(i), type));
          }
          return mlist;
        } else {
          return new Vector<Media>();
        }
      }
    };
  }

  public static PathResource<Update> getUpdate(JSONObject json, String key) {
    return new PathResource<Update>(json, key) {
      private static final long serialVersionUID = 1L;

      @Override
      public Update getResource(ResourcesFactory factory) {
        if (hasResource()) {
          return factory.getUpdate(resource);
        } else {
          return null;
        }
      }
    };
  }
  private final String path;
  protected final JSONValue<?> resource;

  protected PathResource(final JSONObject object, final String key) {
    if (object.contains(key)) {
      path = null;
      resource = object.getJSONObject(key);
    } else {
      path = object.getString(key + "_path");
      resource = null;
    }
  }

  public boolean hasResource() {
    return resource != null;
  }

  public abstract T getResource(ResourcesFactory factory);

  public String getPath() {
    return this.path;
  }
}
